﻿namespace NTLib.Core.Manipulators
{
    using Strings;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Threading;

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TLocker">The type of the locker.</typeparam>
    /// <seealso cref="NTLib.Core.Manipulators.SealedBase{TLocker}" />
    /// <seealso cref="System.Collections.Generic.IEnumerable{T}" />
    /// <seealso cref="System.Collections.Generic.IReadOnlyCollection{T}" />
    /// <seealso cref="System.Collections.Generic.IList{T}" />
    public class SealedCollection<T, TLocker> : SealedBase<TLocker>, IEnumerable<T>, IReadOnlyCollection<T>, IList<T>
        where TLocker : ILocker, new()
    {
        #region Fields

        private readonly List<T> _collection;
        private readonly ReadOnlyCollection<T> _roCollection;

        #endregion

        #region Ctor

        /// <summary>
        /// Initializes a new instance of the <see cref="SealedCollection{T, TLocker}"/> class.
        /// </summary>
        public SealedCollection()
            : this(null, new TLocker())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SealedCollection{T, TLocker}"/> class.
        /// </summary>
        public SealedCollection(IEnumerable<T> collection)
            : this(collection, new TLocker())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SealedCollection{T, TLocker}"/> class.
        /// </summary>
        public SealedCollection(TLocker locker)
            : this(null, locker)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SealedCollection{T, TLocker}"/> class.
        /// </summary>
        public SealedCollection(IEnumerable<T> collection, TLocker locker)
            : base(locker)
        {
            if (collection == null)
                this._collection = new List<T>();
            else
                this._collection = new List<T>(collection);
            this._roCollection = new ReadOnlyCollection<T>(this._collection);
        }

        #endregion

        #region Properties


        /// <summary>
        /// Gets or sets the <see cref="T" /> at the specified index.
        /// </summary>
        /// <value>
        /// The <see cref="T" />.
        /// </value>
        /// <param name="index">The index.</param>
        /// <returns></returns>
        public T this[int index]
        {
            get { return this._roCollection[index]; }

            set
            {
                base.CheckSealed(ExceptionResources.InstanceSealed);
                using (base.LockerImpl.ScopeLock())
                {
                    this._collection[index] = value;
                }
            }
        }

        /// <summary>
        /// Gets the number of elements in the collection.
        /// </summary>
        public int Count
        {
            get { return this._roCollection.Count; }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only.
        /// </summary>
        public bool IsReadOnly
        {
            get { return base.IsSealed; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
        /// </returns>
        /// <exception cref="NotImplementedException"></exception>
        public IEnumerator<T> GetEnumerator()
        {
            using (base.LockerImpl.ScopeLock(true))
            {
                return this._roCollection.GetEnumerator();
            }
        }

        /// <summary>
        /// Determines the index of a specific item in the <see cref="T:System.Collections.Generic.IList`1" />.
        /// </summary>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.IList`1" />.</param>
        /// <returns>
        /// The index of <paramref name="item" /> if found in the list; otherwise, -1.
        /// </returns>
        public int IndexOf(T item)
        {
            base.CheckSealed(ExceptionResources.InstanceSealed);
            using (base.LockerImpl.ScopeLock(true))
            {
                return this._collection.IndexOf(item);
            }
        }

        /// <summary>
        /// Inserts an item to the <see cref="T:System.Collections.Generic.IList`1" /> at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index at which <paramref name="item" /> should be inserted.</param>
        /// <param name="item">The object to insert into the <see cref="T:System.Collections.Generic.IList`1" />.</param>
        public void Insert(int index, T item)
        {
            base.CheckSealed(ExceptionResources.InstanceSealed);
            using (base.LockerImpl.ScopeLock())
            {
                this._collection.Insert(index, item);
            }
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
        /// <returns>
        /// true if <paramref name="item" /> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if <paramref name="item" /> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </returns>
        public bool Remove(T item)
        {
            base.CheckSealed(ExceptionResources.InstanceSealed);
            using (base.LockerImpl.ScopeLock())
            {
                return this._collection.Remove(item);
            }
        }

        /// <summary>
        /// Removes the <see cref="T:System.Collections.Generic.IList`1" /> item at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index of the item to remove.</param>
        public void RemoveAt(int index)
        {
            base.CheckSealed(ExceptionResources.InstanceSealed);
            using (base.LockerImpl.ScopeLock())
            {
                this._collection.RemoveAt(index);
            }
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
        public void Add(T item)
        {
            base.CheckSealed(ExceptionResources.InstanceSealed);

            using (base.LockerImpl.ScopeLock())
            {
                this._collection.Add(item);
            }
        }

        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        /// <param name="items">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
        public void AddRange(IEnumerable<T> items)
        {
            base.CheckSealed(ExceptionResources.InstanceSealed);

            using (base.LockerImpl.ScopeLock())
            {
                foreach (var item in items)
                    this._collection.Add(item);
            }
        }

        /// <summary>
        /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1" />.
        /// </summary>
        public void Clear()
        {
            base.CheckSealed(ExceptionResources.InstanceSealed);
            using (base.LockerImpl.ScopeLock())
            {
                this._collection.Clear();
            }
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1" /> contains a specific value.
        /// </summary>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
        /// <returns>
        /// true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, false.
        /// </returns>
        /// <exception cref="NotImplementedException"></exception>
        public bool Contains(T item)
        {
            using (base.LockerImpl.ScopeLock(true))
            {
                return this._roCollection.Contains(item);
            }
        }

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1" /> to an <see cref="T:System.Array" />, starting at a particular <see cref="T:System.Array" /> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array" /> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1" />. The <see cref="T:System.Array" /> must have zero-based indexing.</param>
        /// <param name="arrayIndex">The zero-based index in <paramref name="array" /> at which copying begins.</param>
        public void CopyTo(T[] array, int arrayIndex)
        {
            using (base.LockerImpl.ScopeLock(true))
            {
                this._roCollection.CopyTo(array, arrayIndex);
            }
        }

        #endregion
    }
}
